// Implementation of a view for icons for Win32.
// Copyright 2006 (C) Ralph Thomas

#include <view/icon.h>
#include <view/win32/common.h>
#include <toolkit/win32/icon-win32.h>
#include <binder.hpp>
#include <iostream>

namespace view {
	class icon {
		adobe::binder_helper_t			_iconBinder;
		const toolkit::iconWin32*		_icon;
		unsigned int					_size;
		HWND							_window;
		void _update( const adobe::value_t& val ) {
			const toolkit::icon* i( 0 );
			//
			// Determine the size that the icon needs to be.
			//
			toolkit::iconWin32::iconSize s( toolkit::iconWin32::kSmall );
			if( _size >= 32 ) s = toolkit::iconWin32::kRegular;
			if( _size >= 48 ) s = toolkit::iconWin32::kLarge;
			if( val.get( i ) ) {
				_icon = static_cast<const toolkit::iconWin32*>( i );
				if( _icon && _window ) {
					SendMessage( _window, STM_SETICON, (WPARAM)_icon->get( s ), 0);
					UpdateWindow( _window );
					EnableWindow( _window, TRUE );
					return;
				}
			}
			EnableWindow( _window, FALSE );
		}
	  public:
		icon(
			const adobe::dictionary_t&		parameters,
			const adobe::widget_node_t&		parent,
			const adobe::factory_token_t&	token )
		: _iconBinder( token.sheet_m ), _icon( 0 ), _size( 48 ), _window( 0 )
		{
			//
			// We need to get the thing to bind to. It's an error for it not
			// to be there.
			//
			adobe::value_t bind( parameters.at( adobe::static_name_t( "bind" ) ) );
			//
			// Bind to the icon. If this fails then it throws an exception, which
			// is good because we're useless without an icon.
			//
			_iconBinder.initialize( bind, boost::bind( &icon::_update, this, _1 ), 0 );
			//
			// Get the size that we should be.
			//
			parameters.get( adobe::static_name_t( "size" ), _size );
			//
			// Create the window that we will draw our icon into. The window
			// is a Win32 "STATIC" control with the SS_ICON style set.
			//
			_window = CreateWindowW( L"STATIC", L"", WS_CHILD | WS_VISIBLE | SS_ICON | SS_CENTERIMAGE, 0, 0,
				_size, _size, getInitialParent(), 0, (HINSTANCE)0, 0 );
			if( !_window ) throw std::runtime_error( "icon: Can't create window" );
		}
		~icon() {
			//
			// We don't delete any icon object ptr that we might have, as it
			// doesn't actually belong to us.
			//
			if( _window ) DestroyWindow( _window );
		}
		adobe::extents_t best_bounds() const {
			adobe::extents_t bounds;
			bounds.width() = _size;
			bounds.height() = _size;
			return bounds;
		}
		HWND getWindow() const { return _window; }
	};

	adobe::widget_node_t icon_factory(
		const adobe::dictionary_t&		parameters,
		const adobe::widget_node_t&		parent,
		const adobe::factory_token_t&	token )
	{
		//
		// Create the icon widget and set it's lifetime.
		//
		icon* i = new icon( parameters, parent, token );
		token.assemblage_m.delete_on_destruction( i );
		//
		// Insert this widget into it's parent on the main display.
		//
		HWND win( i->getWindow() );
		adobe::display_t::position_t display_token( adobe::insert(
			adobe::get_main_display(), parent.display_token_m,
			win ) );
		adobe::display_t::position_t overlay_token(
			token.overlay_m.insert( parent.overlay_token_m ) );
		//
		// Bind the calculate and place signals for Eve.
		//
		adobe::eve_t::signal_suite_t signals;
		signals.eve_calculate_proc_m = boost::bind(
			&eve_client::utility_widget_calculate,
			i->best_bounds(), _1 );
		eve_client::place_func_t placer(
			boost::bind( &place, win, _1, _2 ) );
		signals.eve_place_proc_m = boost::bind(
			&eve_client::utility_widget_place,
			placer, _1, _2,
			boost::ref( token.overlay_m ), overlay_token );
		//
		// Register the widget with the layout engine.
		//
		adobe::eve_t::iterator eve_token(
			token.layout_m.add_view_element( parent.eve_token_m,
				adobe::eve_t::insert_element_t(
					adobe::eve_t::calculate_data_t(),
					false,
					parameters,
					signals )
				)
			);
		//
		// Return the icon view.
		//
		return adobe::widget_node_t( parent.size_m, eve_token,
			display_token, overlay_token );
	}
}
